home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 4 / Macwelt DVD 4.cdr / Entwickler / Mac-OS X / Pantomime / Source / Message.m / Message.m
Encoding:
Text File  |  2002-08-08  |  40.6 KB  |  1,835 lines

  1. /*
  2. **  Message.m
  3. **
  4. **  Copyright (c) 2001, 2002
  5. **
  6. **  Author: Ludovic Marcotte <ludovic@Sophos.ca>
  7. **
  8. **  This library is free software; you can redistribute it and/or
  9. **  modify it under the terms of the GNU Lesser General Public
  10. **  License as published by the Free Software Foundation; either
  11. **  version 2.1 of the License, or (at your option) any later version.
  12. **  
  13. **  This library is distributed in the hope that it will be useful,
  14. **  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. **  Lesser General Public License for more details.
  17. **  
  18. **  You should have received a copy of the GNU Lesser General Public
  19. **  License along with this library; if not, write to the Free Software
  20. **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. #import <Pantomime/Message.h>
  23.  
  24. #import <Pantomime/Constants.h>
  25. #import <Pantomime/Folder.h>
  26. #import <Pantomime/NSStringExtensions.h>
  27. #import <Pantomime/NSDataExtensions.h>
  28.  
  29. #include <time.h>
  30.  
  31. @implementation Message
  32.  
  33. //
  34. //
  35. //
  36. - (id) init
  37. {
  38.   self = [super init];
  39.   
  40.   // We initialize our recipiens array
  41.   recipients = [[NSMutableArray alloc] init];
  42.  
  43.   // We initialize our dictionary that will hold all our headers
  44.   // We initialize it with a capacity of 25. This is a empirical number 
  45.   // that is used to speedup the addition of headers w/o reallocating
  46.   // our array everytime we add a new element.
  47.   headers = [[NSMutableDictionary alloc] initWithCapacity: 25];
  48.   
  49.   // We initialize our headers with some basic values in case they are missing in the messages we decode
  50.   [headers setObject: @"text/plain" forKey: @"Content-Type"];
  51.  
  52.   // FIXME: Should we always initialize with RECENT? or just nothing?
  53.   flags = [[Flags alloc] initWithFlags: RECENT];
  54.  
  55.   // By default, we want the subclass's rawSource method to be called so we set our
  56.   // rawSource ivar to nil. If it's not nil (ONLY set in initWithString), it'll be returned.
  57.   // for performances improvements.
  58.   rawSource = nil;
  59.  
  60.   // We initialize our dictionary holding all extra properties a message might have
  61.   extraProperties = [[NSMutableDictionary alloc] init];
  62.   
  63.   // Initially, we don't have any references
  64.   [self setReferences: nil];
  65.   
  66.  
  67.   [self setInitialized: NO];
  68.   [self setSize: 0];
  69.  
  70.   return self;
  71. }
  72.  
  73.  
  74. //
  75. //
  76. //
  77. - (id) initWithData: (NSData *) theData
  78. {
  79.   NSRange aRange;
  80.  
  81.   aRange = [theData rangeOfCString: "\n\n"];
  82.  
  83.   if (aRange.length == 0)
  84.     {
  85.       NSDebugLog(@"Message: failed to initialize message from data.");
  86.       AUTORELEASE(self);
  87.       return nil;
  88.     }
  89.   
  90.   // We initialize our message with the headers and the content
  91.   self = [self init];
  92.   
  93.   [self setHeadersFromData: [theData subdataWithRange: NSMakeRange(0,aRange.location)]];
  94.   [self setContentFromRawSource:
  95.         [theData subdataWithRange:
  96.                NSMakeRange(aRange.location + 2, [theData length]-(aRange.location+2))]];
  97.   
  98.  
  99.   //
  100.   // We can tell now that this message is fully initialized
  101.   // NOTE: We must NOT call [self setInitialize: YES] since
  102.   //       it will call the method from the subclass and may do
  103.   //       extremely weird things.
  104.   initialized = YES;
  105.  
  106.   // We set our rawSource ivar for performances reason
  107.   [self setRawSource: theData];
  108.  
  109.   return self;
  110. }
  111.  
  112.  
  113. //
  114. //
  115. //
  116. - (id) initWithData: (NSData *) theData
  117.         charset: (NSString *) theCharset
  118. {
  119.   [self setDefaultCharset: theCharset];
  120.  
  121.   return [self initWithData: theData];
  122. }
  123.  
  124.  
  125. //
  126. //
  127. //
  128. - (id) initWithHeadersFromData: (NSData *) theHeaders
  129. {
  130.   self = [self init];
  131.  
  132.   [self setHeadersFromData: theHeaders];
  133.  
  134.   return self;
  135. }
  136.  
  137.  
  138. //
  139. //
  140. //
  141. - (id) initWithHeaders: (NSDictionary *) theHeaders
  142. {
  143.   self = [self init];
  144.   
  145.   [self setHeaders: theHeaders];
  146.   
  147.   return self;
  148. }
  149.  
  150.  
  151. //
  152. //
  153. //
  154. - (void) dealloc
  155. {
  156.   //NSDebugLog(@"Message: -dealloc");
  157.  
  158.   RELEASE(recipients);
  159.   RELEASE(headers);
  160.   RELEASE(flags);
  161.  
  162.   RELEASE(extraProperties);
  163.   
  164.   RELEASE(references);
  165.  
  166.   TEST_RELEASE(rawSource);
  167.   
  168.   [super dealloc];
  169. }
  170.  
  171.  
  172. //
  173. // NSCoding protocol
  174. //
  175. - (void) encodeWithCoder: (NSCoder *) theCoder
  176. {
  177.   [super encodeWithCoder: theCoder];
  178.  
  179.   [theCoder encodeObject: [self recipients]];
  180.   [theCoder encodeObject: [self allHeaders]];
  181.   [theCoder encodeObject: [NSNumber numberWithInt: [self messageNumber]]];
  182.   [theCoder encodeObject: [self flags]];
  183. }
  184.  
  185.  
  186. //
  187. //
  188. //
  189. - (id) initWithCoder: (NSCoder *) theCoder
  190. {
  191.   self = [super init];
  192.   
  193.   [super initWithCoder: theCoder];
  194.  
  195.   [self setRecipients: [theCoder decodeObject]];
  196.   [self setHeaders: [theCoder decodeObject]];
  197.   [self setMessageNumber: [[theCoder decodeObject] intValue]];
  198.   [self setFlags: [theCoder decodeObject]];
  199.  
  200.   // It's very important to set this ivar to NO since we don't serialize the content
  201.   // or our message.
  202.   initialized = NO;
  203.  
  204.   // We initialize the rest of our ivars
  205.   folder = nil;
  206.   rawSource = nil;
  207.  
  208.   // We initialize our dictionary holding all extra properties a message might have
  209.   extraProperties = [[NSMutableDictionary alloc] init];
  210.  
  211.   return self;
  212. }
  213.  
  214.  
  215. //
  216. //
  217. //
  218. - (InternetAddress *) from
  219. {
  220.   return [headers objectForKey: @"From"];
  221. }
  222.  
  223.  
  224. //
  225. //
  226. //
  227. - (void) setFrom: (InternetAddress *) theInternetAddress
  228. {
  229.   [headers setObject: theInternetAddress
  230.        forKey: @"From"];
  231. }
  232.  
  233.  
  234. //
  235. //
  236. //
  237. - (int) messageNumber
  238. {
  239.   return messageNumber;
  240. }
  241.  
  242.  
  243. //
  244. //
  245. //
  246. - (void) setMessageNumber: (int) theMessageNumber
  247. {
  248.   messageNumber = theMessageNumber;
  249. }
  250.  
  251.  
  252. //
  253. //
  254. //
  255. - (NSString *) messageID
  256. {
  257.   return [headers objectForKey: @"Message-ID"];
  258. }
  259.  
  260.  
  261. //
  262. //
  263. //
  264. - (void) setMessageID: (NSString *) theMessageID
  265. {
  266.   [headers setObject: theMessageID
  267.        forKey: @"Message-ID"];
  268. }
  269.  
  270.  
  271. //
  272. //
  273. //
  274. - (NSCalendarDate *) receivedDate
  275. {
  276.   return [headers objectForKey: @"Date"];
  277. }
  278.  
  279.  
  280. //
  281. //
  282. //
  283. - (void) setReceivedDate: (NSCalendarDate*) theDate
  284. {
  285.   [headers setObject: theDate 
  286.        forKey: @"Date"];
  287. }
  288.  
  289.  
  290. //
  291. //
  292. //
  293. - (void) addToRecipients: (InternetAddress *) theAddress
  294. {
  295.   if ( theAddress )
  296.     {
  297.       [recipients addObject: theAddress];
  298.     }
  299. }
  300.  
  301.  
  302. //
  303. //
  304. //
  305. - (void) removeFromRecipients: (InternetAddress *) theAddress
  306. {
  307.   if ( theAddress )
  308.     {
  309.       [recipients removeObject: theAddress];
  310.     }
  311. }
  312.  
  313.  
  314. //
  315. //
  316. //
  317. - (NSArray *) recipients
  318. {
  319.   return recipients;
  320. }
  321.  
  322.  
  323. //
  324. //
  325. //
  326. - (void) setRecipients: (NSArray *) theRecipients
  327. {
  328.   if ( theRecipients )
  329.     {
  330.       NSMutableArray *newRecipients;
  331.       
  332.       newRecipients = [NSMutableArray arrayWithArray: theRecipients];
  333.       RELEASE(recipients);
  334.       RETAIN(newRecipients);
  335.       recipients = newRecipients;
  336.     }
  337.   else
  338.     {
  339.       RELEASE(recipients);
  340.       recipients = nil;
  341.     }
  342. }
  343.  
  344.  
  345. //
  346. //
  347. //
  348. - (int) recipientsCount
  349. {
  350.   return [[self recipients] count];
  351. }
  352.  
  353.  
  354. //
  355. //
  356. //
  357. - (void) removeAllRecipients
  358. {
  359.   [recipients removeAllObjects];
  360. }
  361.  
  362.  
  363. //
  364. //
  365. //
  366. - (InternetAddress *) replyTo
  367. {
  368.   return [headers objectForKey: @"Reply-To"];
  369. }
  370.  
  371.  
  372. //
  373. //
  374. //
  375. - (void) setReplyTo: (InternetAddress *) theInternetAddress
  376. {
  377.   if ( theInternetAddress )
  378.     {
  379.       [headers setObject: theInternetAddress
  380.            forKey: @"Reply-To"];
  381.     }
  382.   else
  383.     {
  384.       [headers removeObjectForKey: @"Reply-To"];
  385.     }
  386. }
  387.  
  388.  
  389. //
  390. //
  391. //
  392. - (NSString *) subject
  393. {
  394.   return [headers objectForKey: @"Subject"];
  395. }
  396.  
  397.  
  398. //
  399. //
  400. //
  401. - (void) setSubject: (NSString *) theSubject
  402. {
  403.   [headers setObject: theSubject
  404.          forKey: @"Subject"];
  405. }
  406.  
  407.  
  408. //
  409. //
  410. //
  411. - (BOOL) isInitialized
  412. {
  413.   return initialized;
  414. }
  415.  
  416.  
  417. //
  418. //
  419. //
  420. - (void) setInitialized: (BOOL) aBOOL
  421. {
  422.   initialized = aBOOL;
  423. }
  424.  
  425.  
  426. //
  427. // Implementation of the Part protocol follows...
  428. //
  429.  
  430.  
  431. - (NSString *) contentDisposition
  432. {
  433.   return [headers objectForKey: @"Content-Disposition"];
  434. }
  435.  
  436. - (void) setContentDisposition: (NSString *) theContentDisposition
  437. {
  438.   [super setContentDisposition: theContentDisposition];
  439.   
  440.   [headers setObject: theContentDisposition
  441.        forKey: @"Content-Disposition"];
  442. }
  443.  
  444. //
  445. //
  446. //
  447. - (NSString *) contentID
  448. {
  449.   return [headers objectForKey: @"Content-Id"];
  450. }        
  451.  
  452.  
  453. //
  454. //
  455. //
  456. - (void) setContentID: (NSString *) theContentID
  457.   [super setContentID: theContentID];
  458.   [headers setObject: theContentID
  459.        forKey: @"Content-Id"];
  460. }  
  461.  
  462.  
  463. //
  464. //
  465. //
  466. - (NSString *) contentType
  467. {
  468.   return [headers objectForKey: @"Content-Type"];
  469. }
  470.  
  471.  
  472. //
  473. //
  474. //
  475. - (void) setContentType: (NSString*) theContentType
  476. {
  477.   [super setContentType: theContentType];
  478.   
  479.   [headers setObject: theContentType
  480.        forKey: @"Content-Type"];
  481. }
  482.  
  483. //
  484. // See RFC1521 (MIME) for more details.
  485. // The Content-Type MUST be defined BEFORE calling this function otherwise
  486. // the content will be considered as a pure string.
  487. //
  488. - (void) setContentFromRawSource: (NSData *) theData
  489. {
  490.   [MimeUtility setContentFromRawSource: theData
  491.            inPart: self];
  492. }
  493.  
  494. //
  495. //
  496. //
  497. - (NSString *) organization
  498. {
  499.   return [headers objectForKey: @"Organization"];
  500. }
  501.  
  502.  
  503. //
  504. //
  505. //
  506. - (void) setOrganization: (NSString *) theOrganization
  507. {
  508.   [headers setObject: theOrganization
  509.        forKey: @"Organization"];
  510. }
  511.  
  512.  
  513. //
  514. //
  515. //
  516. - (id) extraPropertyForKey: (id) theKey
  517. {
  518.   return [extraProperties objectForKey: theKey];
  519. }
  520.  
  521.  
  522. //
  523. //
  524. //
  525. - (void) setExtraProperty: (id) theProperty
  526.                    forKey: (id) theKey
  527. {
  528.   [extraProperties setObject: theProperty
  529.            forKey: theKey];
  530. }
  531.  
  532.  
  533. //
  534. //
  535. //
  536. - (NSArray *) allReferences
  537. {
  538.   return references;
  539. }
  540.  
  541.  
  542. //
  543. //
  544. //
  545. - (void) setReferences: (NSArray *) theReferences
  546. {
  547.   if ( theReferences )
  548.     {
  549.       RETAIN(theReferences);
  550.       RELEASE(references);
  551.       references = theReferences;
  552.     }
  553.   else
  554.     {
  555.       DESTROY(references);
  556.     }
  557. }
  558.  
  559.  
  560. //
  561. //
  562. //
  563. - (Flags *) flags
  564. {
  565.   return flags;
  566. }
  567.  
  568.  
  569. //
  570. //
  571. //
  572. - (void) setFlags: (Flags*) theFlags
  573. {
  574.   RELEASE(flags);
  575.   flags = RETAIN(theFlags);
  576. }
  577.  
  578.  
  579. //
  580. //
  581. //
  582. - (NSString *) mimeVersion
  583. {
  584.   return [headers objectForKey: @"MIME-Version"];
  585. }
  586.  
  587.  
  588. //
  589. //
  590. //
  591. - (void) setMimeVersion: (NSString *) theMimeVersion
  592. {
  593.   [headers setObject: theMimeVersion
  594.        forKey: @"MIME-Version"];
  595. }
  596.  
  597.  
  598. //
  599. // The message will NOT have a content other than the PLAIN/TEXT one(s).
  600. //
  601. - (Message *) replyWithReplyToAll: (BOOL) flag
  602. {
  603.   InternetAddress *anInternetAddress;
  604.   Message *theMessage;
  605.  
  606.   NSMutableString *aMutableString;
  607.   id aContent;
  608.   int i;
  609.  
  610.   BOOL needsToQuote = NO;
  611.   
  612.   theMessage = [[Message alloc] init];
  613.  
  614.   // We set the subject of our message 
  615.   if ( [[[self subject] lowercaseString] hasPrefix: @"re"] )
  616.     {
  617.       [theMessage setSubject: [self subject] ];
  618.     }
  619.   else 
  620.     {
  621.       [theMessage setSubject: [NSString stringWithFormat: @"Re: %@", [self subject]] ];
  622.     }
  623.  
  624.   // If Reply-To is defined, we use it. Otherwise, we use From:
  625.   if ( [self replyTo] == nil )
  626.     {
  627.       anInternetAddress = [self from];
  628.     }
  629.   else 
  630.     {
  631.       anInternetAddress = [self replyTo];
  632.     }
  633.  
  634.   [anInternetAddress setType: TO];
  635.   [theMessage addToRecipients: anInternetAddress];
  636.   
  637.   // We add our In-Reply-To header
  638.   if ( [self messageID] )
  639.     {
  640.       [theMessage addHeader: @"In-Reply-To"
  641.           withValue: [self messageID]];
  642.     }
  643.   
  644.   // If we reply to all, we add the other recipients
  645.   if ( flag )
  646.     {
  647.       NSEnumerator *anEnumerator;
  648.  
  649.       anEnumerator = [[self recipients] objectEnumerator];
  650.       
  651.       while ((anInternetAddress = [anEnumerator nextObject]))
  652.     {
  653.       [anInternetAddress setType: CC];
  654.       [theMessage addToRecipients: anInternetAddress];
  655.     }
  656.     }
  657.  
  658.   // We finally work on the content of the message
  659.   aMutableString = [[NSMutableString alloc] init];
  660.   [aMutableString appendString: @"On "];
  661.   [aMutableString appendString: [[self receivedDate] description] ];
  662.   [aMutableString appendString: [NSString stringWithFormat:@" %@", [[self from] unicodeStringValue]]];
  663.   [aMutableString appendString: @" wrote:\n\n"];
  664.  
  665.   // We give a default value to aContent
  666.   aContent = nil;
  667.   
  668.   //
  669.   // We now get the right text part of the message.
  670.   // 
  671.   // If it's a text/enriched or a text/html, we must remove all the
  672.   // formatting codes and keep only the 'real' text contained in that part.
  673.   //
  674.   if ( [self isMimeType: @"text": @"*"] )
  675.     {
  676.       aContent = (NSString *)[self content];
  677.       needsToQuote = YES;
  678.     }
  679.   //
  680.   // If our message only contains the following part types, we cannot
  681.   // represent those in a reply.
  682.   // 
  683.   else if ( [self isMimeType: @"application": @"*"] ||
  684.         [self isMimeType: @"audio": @"*"] ||
  685.         [self isMimeType: @"image": @"*"] || 
  686.         [self isMimeType: @"message": @"*"] ||
  687.         [self isMimeType: @"video": @"*"] )
  688.     {
  689.       aContent = [NSString stringWithString: @"\t[NON-Text Body part not included]"];
  690.       needsToQuote = NO;
  691.     }
  692.   //
  693.   // We have a multipart type. It can be:
  694.   //
  695.   // multipart/appledouble, multipart/alternative, multipart/related,
  696.   // multipart/mixed or even multipart/report.
  697.   //
  698.   // We must search for a text part to use in our reply.
  699.   //
  700.   else if ( [self isMimeType: @"multipart" : @"*"] )
  701.     {
  702.       MimeMultipart *aMimeMultipart;
  703.       MimeBodyPart *aMimeBodyPart;
  704.       
  705.       aMimeMultipart = (MimeMultipart *)[self content];
  706.       
  707.       for (i = 0; i < [aMimeMultipart count]; i++)
  708.     {
  709.       aMimeBodyPart = [aMimeMultipart bodyPartAtIndex: i];
  710.       
  711.       //
  712.       // We do a full verification on the Content-Type since we might
  713.       // have a text/x-{something} like text/x-vcard.
  714.       //
  715.       if ( [aMimeBodyPart isMimeType: @"text" : @"plain"] ||
  716.            [aMimeBodyPart isMimeType: @"text" : @"enriched"] ||
  717.            [aMimeBodyPart isMimeType: @"text" : @"html"] )
  718.         {
  719.           // If our part was base64 encoded, we must convert the 
  720.           // NSData object into a NSString
  721.           if ( [aMimeBodyPart contentTransferEncoding] == BASE64 &&
  722.            [[aMimeBodyPart content] isKindOfClass: [NSData class]] )
  723.         {
  724.           aContent = [[NSString alloc] initWithData: (NSData *)[aMimeBodyPart content]
  725.                            encoding: NSASCIIStringEncoding];
  726.           AUTORELEASE(aContent);
  727.         }
  728.           else
  729.         {
  730.           aContent = (NSString*)[aMimeBodyPart content];
  731.         }
  732.           break;
  733.         }
  734.       //
  735.       // If we have a multipart/alternative contained in our multipart/mixed (or related)
  736.       // we find the text/plain part contained in this multipart/alternative object.
  737.       //
  738.       else if ( [aMimeBodyPart isMimeType: @"multipart" : @"alternative"] )
  739.         {
  740.           MimeMultipart *anOtherMimeMultipart;
  741.           int j;
  742.           
  743.           anOtherMimeMultipart = (MimeMultipart *)[aMimeBodyPart content];
  744.           
  745.           for (j = 0; j < [anOtherMimeMultipart count]; j++)
  746.         {
  747.           aMimeBodyPart = [anOtherMimeMultipart bodyPartAtIndex: i];
  748.           
  749.           if ( [aMimeBodyPart isMimeType: @"text" : @"plain"] ||
  750.                [aMimeBodyPart isMimeType: @"text" : @"enriched"] ||
  751.                [aMimeBodyPart isMimeType: @"text" : @"html"] )
  752.             {
  753.               // If our part was base64 encoded, we must convert the 
  754.               // NSData object into a NSString
  755.               if ( [aMimeBodyPart contentTransferEncoding] == BASE64 )
  756.             {
  757.               aContent = [[NSString alloc] initWithData: (NSData *)[aMimeBodyPart content]
  758.                               encoding: NSASCIIStringEncoding];
  759.               AUTORELEASE(aContent);
  760.             }
  761.               else
  762.             {
  763.               aContent = (NSString *)[aMimeBodyPart content];
  764.             }
  765.               break;
  766.             } 
  767.         }
  768.         } // else if (...)
  769.     }
  770.       
  771.       needsToQuote = YES;
  772.     }
  773.   
  774.   //
  775.   // It was impossible for use to find a text/plain part. Let's
  776.   // inform our user that we can't do anything with this message.
  777.   //
  778.   if ( !aContent || [aContent isKindOfClass: [NSData class]])
  779.     {
  780.       aContent = [NSString stringWithString: @"\t[NON-Text Body part not included]"];
  781.       needsToQuote = NO;
  782.     }
  783.   else
  784.     {
  785.       // We remove the signature
  786.       NSRange aRange;
  787.       
  788.       aRange = [aContent rangeOfString: @"\n-- "
  789.             options: NSBackwardsSearch];
  790.       
  791.       // We found it!
  792.       if ( aRange.length )
  793.     {
  794.       aContent = [aContent substringToIndex: aRange.location];
  795.     }
  796.     }
  797.  
  798.   // We now have our content as string, let's 'quote' it
  799.   if ( aContent && needsToQuote )
  800.     {
  801.       aContent = [MimeUtility unwrapPlainTextString: aContent
  802.                  usingQuoteWrappingLimit: 78];
  803.       [aMutableString appendString: [MimeUtility quotePlainTextString: aContent
  804.                          quoteLevel: 1
  805.                          wrappingLimit: 80]];
  806.     }
  807.   else if (aContent && !needsToQuote)
  808.     {
  809.       [aMutableString appendString: aContent];
  810.     }
  811.   
  812.   [theMessage setContent: aMutableString];
  813.  
  814.   RELEASE(aMutableString);
  815.   
  816.   return AUTORELEASE(theMessage);
  817. }
  818.  
  819.  
  820. //
  821. // The message WILL have a content.
  822. //
  823. - (Message *) forward
  824. {
  825.   NSMutableString *aMutableString;
  826.   Message *theMessage;
  827.  
  828.   theMessage = [[Message alloc] init];
  829.   
  830.   // We set the subject of our message
  831.   [theMessage setSubject: [NSString stringWithFormat:@"%@ (fwd)", [self subject]] ];
  832.  
  833.   // We create our generic forward message header
  834.   aMutableString = [[NSMutableString alloc] init];
  835.   AUTORELEASE(aMutableString);
  836.  
  837.   [aMutableString appendString: @"---------- Forwarded message ----------\n"];
  838.   [aMutableString appendString: @"Date: "];
  839.   [aMutableString appendString: [[self receivedDate] description] ];
  840.   [aMutableString appendString: @"\nFrom: "];
  841.   [aMutableString appendString: [[self from] unicodeStringValue]];
  842.   [aMutableString appendString: @"\nSubject: "];
  843.   [aMutableString appendString: [NSString stringWithFormat:@"%@\n\n", [self subject]] ];
  844.   
  845.   //
  846.   // If our Content-Type is text/plain, we represent it as a it is, otherwise,
  847.   // we currently create a new body part representing the forwarded message.
  848.   //
  849.   if ( [self isMimeType: @"text": @"*"] )
  850.     {
  851.       // Our message is a text/plain one
  852.       if ( [self isMimeType: @"text" : @"plain"] )
  853.     {
  854.       // We set the content of our message
  855.       [aMutableString appendString: (NSString*)[self content]];
  856.       
  857.       // We set the Content-Transfer-Encoding and the Charset to the previous one
  858.       [theMessage setContentTransferEncoding: [self contentTransferEncoding]];
  859.       [theMessage setCharset: [self charset]];
  860.  
  861.       [theMessage setContent: aMutableString];
  862.       [theMessage setSize: [aMutableString length]];
  863.     }
  864.       // Our message is either a text/enriched, text/html or something else (but text/*).
  865.       else
  866.     {
  867.       MimeMultipart *aMimeMultipart;
  868.       MimeBodyPart *aMimeBodyPart;
  869.  
  870.       aMimeMultipart = [[MimeMultipart alloc] init];
  871.     
  872.       // We add our text/plain part (using Part's default initialized values)
  873.       aMimeBodyPart = [[MimeBodyPart alloc] init];
  874.       [aMimeBodyPart setContent: aMutableString];
  875.       [aMimeBodyPart setContentDisposition: @"inline"];
  876.       [aMimeBodyPart setSize: [aMutableString length]];
  877.       [aMimeMultipart addBodyPart: aMimeBodyPart];
  878.       RELEASE(aMimeBodyPart);
  879.       
  880.       // We add our other text part as a attachment
  881.       aMimeBodyPart = [[MimeBodyPart alloc] init];
  882.       [aMimeBodyPart setContentType: [self contentType]];
  883.       [aMimeBodyPart setContent: [self content]];
  884.       [aMimeBodyPart setContentTransferEncoding: [self contentTransferEncoding]];
  885.       [aMimeBodyPart setCharset: [self charset]];
  886.       [aMimeBodyPart setContentDisposition: @"attachment"];
  887.       [aMimeBodyPart setSize: [self size]];
  888.       [aMimeMultipart addBodyPart: aMimeBodyPart];
  889.       RELEASE(aMimeBodyPart);
  890.  
  891.       [theMessage setContent: aMimeMultipart];
  892.       RELEASE(aMimeMultipart);
  893.     }
  894.     }
  895.   //
  896.   // If our Content-Type is a message/rfc822 or any other type like
  897.   // application/*, audio/*, image/* or video/*
  898.   // 
  899.   else if ( [self isMimeType: @"application": @"*"] ||
  900.         [self isMimeType: @"audio": @"*"] ||
  901.         [self isMimeType: @"image": @"*"] || 
  902.         [self isMimeType: @"message": @"*"] ||
  903.         [self isMimeType: @"video": @"*"] )
  904.     {
  905.       MimeMultipart *aMimeMultipart;
  906.       MimeBodyPart *aMimeBodyPart;
  907.       
  908.       aMimeMultipart = [[MimeMultipart alloc] init];
  909.       
  910.       // We add our text/plain part.
  911.       aMimeBodyPart = [[MimeBodyPart alloc] init];
  912.       [aMimeBodyPart setContent: aMutableString];
  913.       [aMimeBodyPart setContentDisposition: @"inline"];
  914.       [aMimeBodyPart setSize: [aMutableString length]];
  915.       [aMimeMultipart addBodyPart: aMimeBodyPart];
  916.       RELEASE(aMimeBodyPart);
  917.       
  918.       // We add our content as an attachment
  919.       aMimeBodyPart = [[MimeBodyPart alloc] init];  
  920.       [aMimeBodyPart setContentType: [self contentType]];
  921.       [aMimeBodyPart setContent: [self content]];
  922.       [aMimeBodyPart setContentTransferEncoding: [self contentTransferEncoding]];
  923.       [aMimeBodyPart setContentDisposition: @"attachment"];
  924.       [aMimeBodyPart setCharset: [self charset]];
  925.       [aMimeBodyPart setFilename: [self filename]];
  926.       [aMimeBodyPart setSize: [self size]];
  927.       [aMimeMultipart addBodyPart: aMimeBodyPart];
  928.       RELEASE(aMimeBodyPart);
  929.  
  930.       [theMessage setContent: aMimeMultipart];
  931.       RELEASE(aMimeMultipart);
  932.     }
  933.   //
  934.   // We have a multipart object. We must treat multipart/alternative
  935.   // parts differently since we don't want to include multipart parts in the forward.
  936.   //
  937.   else if ( [self isMimeType: @"multipart" : @"*"] )
  938.     {
  939.       //
  940.       // If we have multipart/alternative part, we only keep one part from it.
  941.       //
  942.       if ( [self isMimeType: @"multipart" : @"alternative"] )
  943.     {
  944.       MimeMultipart *aMimeMultipart;
  945.       MimeBodyPart *aMimeBodyPart;
  946.       int i;
  947.       
  948.       aMimeMultipart = (MimeMultipart *)[self content];
  949.       aMimeBodyPart = nil;
  950.  
  951.       // We search for our text/plain part
  952.       for (i = 0; i < [aMimeMultipart count]; i++)
  953.         {
  954.           aMimeBodyPart = [aMimeMultipart bodyPartAtIndex: i];
  955.  
  956.           if ( [aMimeBodyPart isMimeType: @"text": @"plain"] )
  957.         {
  958.           break;
  959.         }
  960.           else
  961.         {
  962.           aMimeBodyPart = nil;
  963.         }
  964.         }
  965.  
  966.       // We found one
  967.       if ( aMimeBodyPart )
  968.         {
  969.           NSString *aString;
  970.           
  971.           // If our part was base64 encoded, we must convert the 
  972.           // NSData object into a NSString
  973.           if ( [aMimeBodyPart contentTransferEncoding] == BASE64 &&
  974.            [[aMimeBodyPart content] isKindOfClass: [NSData class]] )
  975.         {
  976.           aString = [[NSString alloc] initWithData: (NSData *)[aMimeBodyPart content]
  977.                           encoding: NSASCIIStringEncoding];
  978.           AUTORELEASE(aString);
  979.         }
  980.           else
  981.         {
  982.           aString = (NSString*)[aMimeBodyPart content];
  983.         }
  984.  
  985.           // We set the content of our message
  986.           [aMutableString appendString: aString];
  987.           
  988.           // We set the Content-Transfer-Encoding and the Charset to our text part
  989.           [theMessage setContentTransferEncoding: [aMimeBodyPart contentTransferEncoding]];
  990.           [theMessage setCharset: [aMimeBodyPart charset]];
  991.           
  992.           [theMessage setContent: aMutableString];
  993.           [theMessage setSize: [aMutableString length]];
  994.         }
  995.       // We haven't found one! Inform the user that it happened.
  996.       else
  997.         {
  998.           [aMutableString appendString: @"No text/plain part from this multipart/alternative part has been found"];
  999.           [aMutableString appendString: @"\nNo parts have been included as attachments with this mail during the forward operation."];
  1000.           [aMutableString appendString: @"\n\nPlease report this as a bug."];
  1001.  
  1002.           [theMessage setContent: aMutableString];
  1003.           [theMessage setSize: [aMutableString length]];
  1004.         }
  1005.     }
  1006.       //
  1007.       // We surely have a multipart/mixed or multipart/related.
  1008.       // We search for a text/plain part inside our multipart object.
  1009.       // We 'keep' the other parts in a separate new multipart object too
  1010.       // that will become our new content.
  1011.       //
  1012.       else
  1013.     {
  1014.       MimeMultipart *aMimeMultipart, *newMimeMultipart;
  1015.       MimeBodyPart *aMimeBodyPart;
  1016.       BOOL hasFoundTextPlain = NO;
  1017.       int i;
  1018.       
  1019.       // We get our current mutipart object
  1020.       aMimeMultipart = (MimeMultipart *)[self content];
  1021.  
  1022.       // We create our new multipart object for holding all our parts.
  1023.       newMimeMultipart = [[MimeMultipart alloc] init];
  1024.       
  1025.       for (i = 0; i < [aMimeMultipart count]; i++)
  1026.         {
  1027.           aMimeBodyPart = [aMimeMultipart bodyPartAtIndex: i];
  1028.           
  1029.           if ( [aMimeBodyPart isMimeType: @"text": @"plain"] 
  1030.            && !hasFoundTextPlain )
  1031.         {
  1032.           MimeBodyPart *newMimeBodyPart;
  1033.           
  1034.           newMimeBodyPart = [[MimeBodyPart alloc] init];
  1035.           
  1036.           // We set the content of our new part
  1037.           [aMutableString appendString: (NSString*)[aMimeBodyPart content]];
  1038.           [newMimeBodyPart setContent: aMutableString];
  1039.           [newMimeBodyPart setSize: [aMutableString length]];
  1040.           
  1041.           // We set the Content-Transfer-Encoding and the Charset to the previous one
  1042.           [newMimeBodyPart setContentTransferEncoding: [aMimeBodyPart contentTransferEncoding]];
  1043.           [newMimeBodyPart setCharset: [aMimeBodyPart charset]];
  1044.           
  1045.           // We finally add our new part to our MIME multipart object
  1046.           [newMimeMultipart addBodyPart: newMimeBodyPart];
  1047.           RELEASE(newMimeBodyPart);
  1048.           
  1049.           hasFoundTextPlain = YES;
  1050.         }
  1051.           // We set the Content-Disposition to "attachment"
  1052.           // all the time.
  1053.           else
  1054.         {
  1055.           [aMimeBodyPart setContentDisposition: @"attachment"];
  1056.           [newMimeMultipart addBodyPart: aMimeBodyPart];
  1057.         }
  1058.         }
  1059.       
  1060.       [theMessage setContent: newMimeMultipart];
  1061.       RELEASE(newMimeMultipart);
  1062.     }
  1063.     }
  1064.   //
  1065.   // We got an unknown part. Let's inform the user about this situation.
  1066.   //
  1067.   else
  1068.     {
  1069.       // We set the content of our message
  1070.       [aMutableString appendString: @"The original message contained an unknown part that was not included in this forward message."];
  1071.       [aMutableString appendString: @"\n\nPlease report this as a bug."];
  1072.       
  1073.       [theMessage setContent: aMutableString];
  1074.       [theMessage setSize: [aMutableString length]];
  1075.     }
  1076.   
  1077.   return AUTORELEASE(theMessage);
  1078. }
  1079.  
  1080.  
  1081. //
  1082. //
  1083. //
  1084. - (NSData *) dataUsingSendingMode : (int) theMode
  1085. {
  1086.   NSMutableData *aMutableData;
  1087.   NSDictionary *aLocale;
  1088.  
  1089.   NSEnumerator *allHeaderKeyEnumerator;
  1090.   NSString *aKey;
  1091.  
  1092.   NSCalendarDate *aCalendarDate;
  1093.  
  1094.   NSData *aBoundary, *aData;
  1095.  
  1096.   char *lineTerminator;
  1097.  
  1098.  
  1099.   if ( theMode == SEND_USING_SMTP )
  1100.     {
  1101.       lineTerminator = CRLF;
  1102.     }
  1103.   else
  1104.     {
  1105.       lineTerminator = LF;
  1106.     }
  1107.  
  1108.   // We get our locale in English
  1109. #ifndef MACOSX
  1110.   aLocale = [NSDictionary dictionaryWithContentsOfFile: [NSBundle pathForGNUstepResource: @"English"
  1111.                                   ofType: nil
  1112.                                   inDirectory: @"Resources/Languages"] ];
  1113. #else
  1114.   aLocale = [NSDictionary dictionaryWithContentsOfFile: [[NSBundle bundleForClass:[NSObject class]]
  1115.                               pathForResource: @"English"
  1116.                               ofType: nil
  1117.                               inDirectory: @"Languages"] ];
  1118. #endif
  1119.   
  1120.   // We initialize our mutable data object holding the raw data of the
  1121.   // new message.
  1122.   aMutableData = [[NSMutableData alloc] init];
  1123.   
  1124.   aBoundary = [MimeUtility generateBoundary];
  1125.   
  1126.   // If we're sending this message to a local folder, we must append the From - tag.
  1127.   if ( theMode == SEND_TO_FOLDER )
  1128.     {
  1129.       [aMutableData appendCFormat: @"From -\n"];
  1130.     }
  1131.  
  1132. #ifndef MACOSX
  1133.   tzset();
  1134.  
  1135.   aCalendarDate = [[[NSDate alloc] init] dateWithCalendarFormat:@"%a, %d %b %Y %H:%M:%S %z"
  1136.                      timeZone: [NSTimeZone timeZoneWithAbbreviation: 
  1137.                                  [NSString stringWithCString: tzname[1]]] ];
  1138. #else
  1139.   aCalendarDate = [[[NSDate alloc] init] dateWithCalendarFormat:@"%a, %d %b %Y %H:%M:%S %z"
  1140.                      timeZone: [NSTimeZone systemTimeZone] ];
  1141. #endif
  1142.   [aMutableData appendCFormat: @"Date: %@%s", [aCalendarDate descriptionWithLocale: aLocale],
  1143.         lineTerminator];
  1144.   
  1145.   // We set the subject, if we have one!
  1146.   if ( [[[self subject] stringByTrimmingWhiteSpaces] length] > 0 )
  1147.     {
  1148.       [aMutableData appendCString: "Subject: "];
  1149.       [aMutableData appendData: [MimeUtility encodeWordUsingQuotedPrintable: [self subject]
  1150.                                       prefixLength: 8]];
  1151.       [aMutableData appendCString: lineTerminator];
  1152.     }
  1153.   
  1154.   // We set our Message-ID
  1155.   [aMutableData appendCFormat:@"Message-ID: <"];
  1156.   [aMutableData appendData: [MimeUtility generateOSID]];
  1157.   [aMutableData appendCFormat: @">%s", lineTerminator];
  1158.   
  1159.   [aMutableData appendCFormat: @"MIME-Version: 1.0 (Generated by Pantomime %@)%s", PANTOMIME_VERSION, lineTerminator];
  1160.  
  1161.   // We encode our From: field
  1162.   [aMutableData appendCFormat: @"From: "];
  1163.   [aMutableData appendData: [[self from] dataValue]];
  1164.   [aMutableData appendCFormat: @"%s",lineTerminator];
  1165.   
  1166.   // We encode our To field
  1167.   aData = [self _formatRecipientsWithType: TO];
  1168.   
  1169.   if ( aData )
  1170.     {
  1171.       [aMutableData appendCString: "To: "];
  1172.       [aMutableData appendData: aData];
  1173.       [aMutableData appendCString: lineTerminator];
  1174.     }
  1175.  
  1176.   // We encode our Cc field
  1177.   aData = [self _formatRecipientsWithType: CC];
  1178.   
  1179.   if ( aData )
  1180.     {
  1181.       [aMutableData appendCString: "Cc: "];
  1182.       [aMutableData appendData: aData];
  1183.       [aMutableData appendCString: lineTerminator];
  1184.     }
  1185.  
  1186.   // We encode our Bcc field
  1187.   aData = [self _formatRecipientsWithType: BCC];
  1188.   
  1189.   if ( aData )
  1190.     {
  1191.       [aMutableData appendCString: "Bcc: "];
  1192.       [aMutableData appendData: aData];
  1193.       [aMutableData appendCString: lineTerminator];
  1194.     }
  1195.   
  1196.   // We set the Reply-To address in case we need to
  1197.   if ( [self replyTo] )
  1198.     {
  1199.       [aMutableData appendCFormat: @"Reply-To: "];
  1200.       [aMutableData appendData: [[self replyTo] dataValue]];
  1201.       [aMutableData appendCFormat: @"%s",lineTerminator];
  1202.     }
  1203.   
  1204.   // We set the Organization header value if we need to
  1205.   // FIXME: Should we quote this value?
  1206.   if ( [self organization] )
  1207.     {
  1208.       [aMutableData appendCFormat:@"Organization: %@%s", [self organization], lineTerminator];
  1209.     }
  1210.   
  1211.   // We set the In-Reply-To header if we need to
  1212.   if ( [self headerValueForName: @"In-Reply-To"] )
  1213.     {
  1214.       [aMutableData appendCFormat:@"In-Reply-To: %@%s", [self headerValueForName: @"In-Reply-To"], lineTerminator];
  1215.     }
  1216.   
  1217.  
  1218.   // We now set all X-* headers
  1219.   allHeaderKeyEnumerator = [[self allHeaders] keyEnumerator];
  1220.   
  1221.   while ( (aKey = [allHeaderKeyEnumerator nextObject]) ) 
  1222.     {
  1223.       if ( [aKey hasPrefix: @"X-"] )
  1224.     {
  1225.       [aMutableData appendCFormat:@"%@: %@%s", aKey, [self headerValueForName: aKey], lineTerminator];
  1226.     }
  1227.     }
  1228.   
  1229.   //
  1230.   // We add our message header/body separator
  1231.   //
  1232.   [aMutableData appendData: [super dataUsingSendingMode: theMode]];
  1233.  
  1234.   return AUTORELEASE(aMutableData);
  1235. }
  1236.  
  1237.  
  1238. //
  1239. // This method is used to add an extra header to the list of headers
  1240. // of the message.
  1241. //
  1242. - (void) addHeader: (NSString *) theName
  1243.      withValue: (NSString *) theValue
  1244. {
  1245.   [headers setObject: theValue forKey: theName];
  1246. }
  1247.  
  1248.  
  1249. - (id) headerValueForName: (NSString *) theName
  1250. {
  1251.   return [headers objectForKey: theName];
  1252. }
  1253.  
  1254. - (NSDictionary *) allHeaders
  1255. {
  1256.   return headers;
  1257. }
  1258.  
  1259. - (Folder *) folder
  1260. {
  1261.   return folder;
  1262. }
  1263.  
  1264. - (void) setFolder: (Folder *) theFolder
  1265. {
  1266.   folder = theFolder;
  1267. }
  1268.  
  1269.  
  1270. //
  1271. // This method initalize all the headers of a message
  1272. // from a raw data source.
  1273. //
  1274. // FIXME: Should we move most of the 'parsing' to the
  1275. // Parser class?
  1276. //
  1277. - (void) setHeadersFromData: (NSData *) theHeaders
  1278. {
  1279.   NSAutoreleasePool *pool;
  1280.   NSArray *allLines;
  1281.   int i;
  1282.   
  1283.   if (!theHeaders || [theHeaders length] == 0)
  1284.     {
  1285.       return;
  1286.     }
  1287.  
  1288.   // We initialize a local autorelease pool
  1289.   pool = [[NSAutoreleasePool alloc] init];
  1290.  
  1291.   // We MUST be sure to unfold all headers properly before
  1292.   // decoding the headers
  1293.   theHeaders = [MimeUtility unfoldLinesFromData: theHeaders];
  1294.  
  1295.   allLines = [theHeaders componentsSeparatedByCString: "\n"];
  1296.  
  1297.   for (i = 0; i < [allLines count]; i++)
  1298.     {
  1299.       NSData *aLine = [allLines objectAtIndex: i];
  1300.  
  1301.       // We stop if we found the header separator. (\n\n) since someone could
  1302.       // have called this method with the entire rawsource of a message.
  1303.       if ( [aLine length]==0 )
  1304.     {
  1305.       break;
  1306.     }
  1307.  
  1308.       if ([aLine hasCaseInsensitiveCPrefix: "Bcc"])
  1309.     {
  1310.       [Parser parseDestination:aLine
  1311.           forType:BCC
  1312.           inMessage:self];
  1313.     }
  1314.       else if ([aLine hasCaseInsensitiveCPrefix: "Cc"])
  1315.     {
  1316.       [Parser parseDestination:aLine
  1317.           forType:CC
  1318.           inMessage:self];
  1319.     }
  1320.       else if ([aLine hasCaseInsensitiveCPrefix: "Content-Disposition"])
  1321.     {
  1322.       [Parser parseContentDisposition:aLine inPart:self];
  1323.     }
  1324.       else if ([aLine hasCaseInsensitiveCPrefix: "Content-Transfer-Encoding"])
  1325.     {
  1326.       [Parser parseContentTransferEncoding:aLine inPart:self];
  1327.     }
  1328.       else if ([aLine hasCaseInsensitiveCPrefix: "Content-Type"])
  1329.     {
  1330.       [Parser parseContentType:aLine inPart:self];
  1331.     }
  1332.       else if ([aLine hasCaseInsensitiveCPrefix: "Date"])
  1333.     {
  1334.       [Parser parseDate:aLine inMessage:self];
  1335.     }
  1336.       else if ( [aLine hasCaseInsensitiveCPrefix: "From"] &&
  1337.         ![aLine hasCaseInsensitiveCPrefix: "From -"] )
  1338.     {
  1339.       [Parser parseFrom:aLine inMessage:self];
  1340.     }
  1341.       else if ([aLine hasCaseInsensitiveCPrefix: "Message-ID"])
  1342.     {
  1343.       [Parser parseMessageID:aLine inMessage:self];
  1344.     }
  1345.       else if ([aLine hasCaseInsensitiveCPrefix: "MIME-Version"])
  1346.     {
  1347.       [Parser parseMimeVersion:aLine inMessage:self];
  1348.     }
  1349.       else if ([aLine hasCaseInsensitiveCPrefix: "Organization"])
  1350.     {
  1351.       [Parser parseOrganization:aLine inMessage:self];
  1352.     }
  1353.       else if ([aLine hasCaseInsensitiveCPrefix: "References"])
  1354.     {
  1355.       [Parser parseReferences: aLine 
  1356.           inMessage: self];
  1357.     }
  1358.       else if ([aLine hasCaseInsensitiveCPrefix: "Reply-To"])
  1359.     {
  1360.       [Parser parseReplyTo:aLine inMessage:self];
  1361.     }
  1362.       else if ([aLine hasCaseInsensitiveCPrefix: "Resent-From"])
  1363.     {
  1364.       [Parser parseResentFrom:aLine inMessage:self];
  1365.     }
  1366.       else if ([aLine hasCaseInsensitiveCPrefix: "Resent-Bcc"])
  1367.     {
  1368.       [Parser parseDestination:aLine
  1369.           forType:RESENT_BCC
  1370.           inMessage:self];
  1371.     }
  1372.       else if ([aLine hasCaseInsensitiveCPrefix: "Resent-Cc"])
  1373.     {
  1374.       [Parser parseDestination:aLine
  1375.           forType:RESENT_CC
  1376.           inMessage:self];
  1377.     }
  1378.       else if ([aLine hasCaseInsensitiveCPrefix: "Resent-To"])
  1379.     {
  1380.       [Parser parseDestination:aLine
  1381.           forType:RESENT_TO
  1382.           inMessage:self];
  1383.     }
  1384.       else if ([aLine hasCaseInsensitiveCPrefix: "Status"])
  1385.     {
  1386.       [Parser parseStatus:aLine inMessage:self];
  1387.     }
  1388.       else if ([aLine hasCaseInsensitiveCPrefix: "To"])
  1389.     {
  1390.       [Parser parseDestination:aLine
  1391.           forType:TO
  1392.           inMessage:self];
  1393.     }
  1394.       else if ([aLine hasCaseInsensitiveCPrefix: "X-Status"])
  1395.     {
  1396.       [Parser parseXStatus:aLine inMessage:self];
  1397.     }
  1398.       else if ([aLine hasCaseInsensitiveCPrefix: "Subject"])
  1399.     {
  1400.       [Parser parseSubject:aLine inMessage:self];
  1401.     }
  1402.       else
  1403.     {
  1404.       [Parser parseUnknownHeader:aLine inMessage:self];
  1405.     }
  1406.     }
  1407.  
  1408.   RELEASE(pool);
  1409. }
  1410.  
  1411. //
  1412. //
  1413. //
  1414. - (void) setHeaders: (NSDictionary *) theHeaders
  1415. {
  1416.   RELEASE(headers);
  1417.   
  1418.   headers = [[NSMutableDictionary alloc] initWithCapacity: [theHeaders count]];
  1419.   [headers addEntriesFromDictionary: theHeaders];
  1420. }
  1421.  
  1422.  
  1423. //
  1424. // This method is used to optain tha raw source of a message.
  1425. // It's returned as a NSData object. The returned data should
  1426. // be the message like it's achived in a mbox format and it could
  1427. // easily be decoded with Message: -initWithData.
  1428. // 
  1429. // All subclasses of Message MUST implement this method.
  1430. //
  1431. - (NSData *) rawSource
  1432. {
  1433.   if ( !rawSource )
  1434.     {
  1435.       [self subclassResponsibility: _cmd];
  1436.       return nil;
  1437.     }
  1438.   
  1439.   return rawSource;
  1440. }
  1441.  
  1442.  
  1443. //
  1444. //
  1445. //
  1446. - (void) setRawSource: (NSData *) theRawSource
  1447. {
  1448.   if ( theRawSource )
  1449.     {
  1450.       RETAIN(theRawSource);
  1451.       RELEASE(rawSource);
  1452.       rawSource = theRawSource;
  1453.     }
  1454.   else
  1455.     {
  1456.       DESTROY(rawSource);
  1457.     }
  1458. }
  1459.  
  1460. //
  1461. //
  1462. //
  1463. - (NSCalendarDate *) resentDate
  1464. {
  1465.   return [headers objectForKey: @"Resent-Date"];
  1466. }
  1467.  
  1468. - (void) setResentDate: (NSCalendarDate *) theResentDate
  1469. {
  1470.   [headers setObject: theResentDate 
  1471.        forKey: @"Resent-Date"];
  1472. }
  1473.  
  1474. - (InternetAddress *) resentFrom
  1475. {
  1476.   return [headers objectForKey: @"Resent-From"];
  1477. }
  1478.  
  1479. - (void) setResentFrom: (InternetAddress *) theInternetAddress
  1480. {
  1481.   [headers setObject: theInternetAddress
  1482.        forKey: @"Resent-From"];
  1483. }
  1484.  
  1485. - (NSString *) resentMessageID
  1486. {
  1487.   return [headers objectForKey: @"Resent-Message-ID"];
  1488. }
  1489.  
  1490. - (void) setResentMessageID: (NSString *) theResentMessageID
  1491. {
  1492.   [headers setObject: theResentMessageID
  1493.        forKey: @"Resent-Message-ID"];
  1494. }
  1495.  
  1496. - (NSString *) resentSubject
  1497. {
  1498.   return [headers objectForKey: @"Resent-Subject"];
  1499. }
  1500.  
  1501. - (void) setResentSubject: (NSString *) theResentSubject
  1502. {
  1503.   [headers setObject: theResentSubject
  1504.        forKey: @"Resent-Subject"];
  1505. }
  1506.  
  1507. @end
  1508.  
  1509.  
  1510. //
  1511. // Message's sorting category
  1512. //
  1513. @implementation Message (Comparing)
  1514.  
  1515. - (int) compareAccordingToNumber: (Message *) aMessage
  1516. {
  1517.   int num1, num2;
  1518.   num1 = [self messageNumber];
  1519.   num2 = [aMessage messageNumber];
  1520.   if (num1 < num2)
  1521.     {
  1522.       return NSOrderedAscending;
  1523.     }
  1524.   else if (num1 > num2)
  1525.     {
  1526.       return NSOrderedDescending;
  1527.     }
  1528.   else
  1529.     {
  1530.       return NSOrderedSame;
  1531.     }
  1532. }
  1533.  
  1534. - (int) reverseCompareAccordingToNumber: (Message *) aMessage
  1535. {
  1536.   int num1, num2;
  1537.   num2 = [self messageNumber];
  1538.   num1 = [aMessage messageNumber];
  1539.   if (num1 < num2)
  1540.     {
  1541.       return NSOrderedAscending;
  1542.     }
  1543.   else if (num1 > num2)
  1544.     {
  1545.       return NSOrderedDescending;
  1546.     }
  1547.   else
  1548.     {
  1549.       return NSOrderedSame;
  1550.     }
  1551. }
  1552.  
  1553. - (int) compareAccordingToDate: (Message *) aMessage
  1554. {
  1555.   NSDate *date1 = [self receivedDate];
  1556.   NSDate *date2 = [aMessage receivedDate];
  1557.   NSTimeInterval timeInterval;
  1558.  
  1559.   if (date1 == nil || date2 == nil)
  1560.     {
  1561.       return [self compareAccordingToNumber: aMessage]; 
  1562.     }
  1563.  
  1564.   timeInterval = [date1 timeIntervalSinceDate: date2];
  1565.  
  1566.   if (timeInterval < 0)
  1567.     {
  1568.       return NSOrderedAscending;
  1569.     }
  1570.   else if (timeInterval > 0)
  1571.     {
  1572.       return NSOrderedDescending;
  1573.     }
  1574.   else
  1575.     {
  1576.       return [self compareAccordingToNumber: aMessage];      
  1577.     }
  1578. }
  1579.  
  1580. - (int) reverseCompareAccordingToDate: (Message *) aMessage
  1581. {
  1582.   NSDate *date2 = [self receivedDate];
  1583.   NSDate *date1 = [aMessage receivedDate];
  1584.   NSTimeInterval timeInterval;
  1585.  
  1586.   if (date1 == nil || date2 == nil)
  1587.     {
  1588.       return [self reverseCompareAccordingToNumber: aMessage]; 
  1589.     }
  1590.  
  1591.   timeInterval = [date1 timeIntervalSinceDate: date2];
  1592.  
  1593.   if (timeInterval < 0)
  1594.     {
  1595.       return NSOrderedAscending;
  1596.     }
  1597.   else if (timeInterval > 0)
  1598.     {
  1599.       return NSOrderedDescending;
  1600.     }
  1601.   else
  1602.     {
  1603.       return [self reverseCompareAccordingToNumber: aMessage];      
  1604.     }
  1605. }
  1606.  
  1607. - (int) compareAccordingToSender: (Message *) aMessage
  1608. {
  1609.   InternetAddress *from1, *from2;
  1610.   NSString *fromString1, *fromString2;
  1611.   NSString *tempString;
  1612.   int result;
  1613.  
  1614.   from1 = [self from];
  1615.   from2 = [aMessage from];
  1616.  
  1617.   tempString = [from1 personal];
  1618.   if (tempString == nil || [tempString length] == 0)
  1619.     {
  1620.       fromString1 = [from1 address];
  1621.       if (fromString1 == nil)
  1622.     fromString1 = @"";
  1623.     }
  1624.   else
  1625.     {
  1626.       fromString1 = tempString;
  1627.     }
  1628.  
  1629.  
  1630.   tempString = [from2 personal];
  1631.   if (tempString == nil || [tempString length] == 0)
  1632.     {
  1633.       fromString2 = [from2 address];
  1634.       if (fromString2 == nil)
  1635.     fromString2 = @"";
  1636.     }
  1637.   else
  1638.     {
  1639.       fromString2 = tempString;
  1640.     }
  1641.  
  1642.   result = [fromString1 caseInsensitiveCompare: fromString2];
  1643.   if (result == NSOrderedSame)
  1644.     {
  1645.       return [self compareAccordingToNumber: aMessage];
  1646.     }
  1647.   else
  1648.     {
  1649.       return result;
  1650.     }
  1651. }
  1652.  
  1653. - (int) reverseCompareAccordingToSender: (Message *) aMessage
  1654. {
  1655.   InternetAddress *from1, *from2;
  1656.   NSString *fromString1, *fromString2;
  1657.   NSString *tempString;
  1658.   int result;
  1659.  
  1660.   from2 = [self from];
  1661.   from1 = [aMessage from];
  1662.  
  1663.   tempString = [from1 personal];
  1664.   if (tempString == nil || [tempString length] == 0)
  1665.     {
  1666.       fromString1 = [from1 address];
  1667.       if (fromString1 == nil)
  1668.     fromString1 = @"";
  1669.     }
  1670.   else
  1671.     {
  1672.       fromString1 = tempString;
  1673.     }
  1674.  
  1675.  
  1676.   tempString = [from2 personal];
  1677.   if (tempString == nil || [tempString length] == 0)
  1678.     {
  1679.       fromString2 = [from2 address];
  1680.       if (fromString2 == nil)
  1681.     fromString2 = @"";
  1682.     }
  1683.   else
  1684.     {
  1685.       fromString2 = tempString;
  1686.     }
  1687.  
  1688.  
  1689.   result = [fromString1 caseInsensitiveCompare: fromString2];
  1690.   
  1691.   if (result == NSOrderedSame)
  1692.     {
  1693.       return [self reverseCompareAccordingToNumber: aMessage];
  1694.     }
  1695.   else
  1696.     {
  1697.       return result;
  1698.     }
  1699. }
  1700.  
  1701. - (int) compareAccordingToSubject: (Message *) aMessage
  1702. {
  1703.   NSString *subject1 = [self subject];
  1704.   NSString *subject2 = [aMessage subject];
  1705.   int result;
  1706.   
  1707.   if (subject1 == nil)
  1708.     subject1 = @"";
  1709.   if (subject2 == nil)
  1710.     subject2 = @"";
  1711.  
  1712.   result = [subject1 caseInsensitiveCompare: subject2];
  1713.  
  1714.   if (result == NSOrderedSame)
  1715.     {
  1716.       return [self compareAccordingToNumber: aMessage];      
  1717.     }
  1718.   else
  1719.     {
  1720.       return result;
  1721.     }
  1722. }
  1723.  
  1724. - (int) reverseCompareAccordingToSubject: (Message *) aMessage
  1725. {
  1726.   NSString *subject2 = [self subject];
  1727.   NSString *subject1 = [aMessage subject];
  1728.   int result;
  1729.   
  1730.   if (subject1 == nil)
  1731.     subject1 = @"";
  1732.   if (subject2 == nil)
  1733.     subject2 = @"";
  1734.  
  1735.   result = [subject1 caseInsensitiveCompare: subject2];
  1736.  
  1737.   if (result == NSOrderedSame)
  1738.     {
  1739.       return [self compareAccordingToNumber: aMessage];      
  1740.     }
  1741.   else
  1742.     {
  1743.       return result;
  1744.     }
  1745. }
  1746.  
  1747. - (int) compareAccordingToSize: (Message *) aMessage
  1748. {
  1749.   int size1 = [self size];
  1750.   int size2 = [aMessage size];
  1751.  
  1752.  
  1753.   if (size1 < size2)
  1754.     {
  1755.       return NSOrderedAscending;
  1756.     }
  1757.   else if (size1 > size2)
  1758.     {
  1759.       return NSOrderedDescending;
  1760.     }
  1761.   else
  1762.     {
  1763.       return [self compareAccordingToNumber: aMessage];
  1764.     }
  1765. }
  1766.  
  1767. - (int) reverseCompareAccordingToSize: (Message *) aMessage
  1768. {
  1769.   int size1 = [aMessage size];
  1770.   int size2 = [self size];
  1771.  
  1772.  
  1773.   if (size1 < size2)
  1774.     {
  1775.       return NSOrderedAscending;
  1776.     }
  1777.   else if (size1 > size2)
  1778.     {
  1779.       return NSOrderedDescending;
  1780.     }
  1781.   else
  1782.     {
  1783.       return [self reverseCompareAccordingToNumber: aMessage];      
  1784.     }
  1785. }
  1786.  
  1787. @end
  1788.  
  1789.  
  1790. //
  1791. // Private methods
  1792. //
  1793. @implementation Message (Private)
  1794.  
  1795. - (NSData *) _formatRecipientsWithType: (int) theType
  1796. {
  1797.   NSMutableData *aMutableData;
  1798.   NSArray *anArray;
  1799.   int i;
  1800.  
  1801.   aMutableData = [[NSMutableData alloc] init];
  1802.   anArray = [self recipients];
  1803.  
  1804.   for (i = 0; i < [anArray count]; i++)
  1805.     {
  1806.       InternetAddress *anInternetAddress;
  1807.  
  1808.       anInternetAddress = [anArray objectAtIndex: i];
  1809.  
  1810.       if ([anInternetAddress type] == theType)
  1811.     {
  1812.       [aMutableData appendData: [anInternetAddress dataValue]];
  1813.       [aMutableData appendCString: ", "];
  1814.     }
  1815.     }
  1816.   
  1817.   if ( [aMutableData length] > 0)
  1818.     {
  1819.       [aMutableData setLength: [aMutableData length] - 2];
  1820.  
  1821.       return AUTORELEASE(aMutableData);
  1822.     }
  1823.   else
  1824.     {
  1825.       RELEASE(aMutableData);
  1826.       
  1827.       return nil;
  1828.     }
  1829. }
  1830.  
  1831. @end
  1832.  
  1833.  
  1834.